using System.IO;
using System.Reflection;
using AutoTest.Core.Infrastructure.Files;
using AutoTest.Core.Infrastructure.Messaging;
using AutoTest.Core.Utils;
using log4net;

namespace AutoTest.Core.Domain.TestFinding
{
    public class TestFinder : ITestFinder, IConsumerOf<FileChangedMessage>
    {
        private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private readonly IAutoTestConfiguration _configuration;
        private readonly IMessageBus _messageBus;
        private readonly ICSharpInspector _inspector;
        private readonly IFileSystem _fileSystem;

        public TestFinder(IAutoTestConfiguration configuration, IMessageBus messageBus, ICSharpInspector inspector, IFileSystem fileSystem)
        {
            _configuration = configuration;
            _fileSystem = fileSystem;
            _inspector = inspector;
            _messageBus = messageBus;
        }

        // TODO: BL: support test files with multiple classes

        public void Consume(FileChangedMessage message)
        {
            var name = Path.GetFileNameWithoutExtension(message.File.Name);

            if (name.EndsWith("Test") || name.EndsWith("Tests"))
            {
                ExtractTestNameAndPublishRunTestMessage(message.File.FullName);
                return;
            }

            var testFilename = name + "Test" + message.File.Extension;
            var foundTestFilename = _fileSystem.FindFileInDirectoryTree(testFilename, _configuration.FolderToWatch);
            if (foundTestFilename.NotNullOrEmptyTrimmed())
            {
                ExtractTestNameAndPublishRunTestMessage(foundTestFilename);
                return;
            }

            var testsFilename = name + "Tests" + message.File.Extension;
            var foundTestsFilename = _fileSystem.FindFileInDirectoryTree(testsFilename, _configuration.FolderToWatch);
            if (foundTestsFilename.NotNullOrEmptyTrimmed())
            {
                ExtractTestNameAndPublishRunTestMessage(foundTestsFilename);
                return;
            }
        }

        private void ExtractTestNameAndPublishRunTestMessage(string filename)
        {
            var ns = _inspector.GetNamespace(filename);
            var className = _inspector.GetFirstPublicClass(filename);
            if (ns.IsNullOrEmptyTrimmed() || className.IsNullOrEmptyTrimmed())
                return;

            var testName = ns + "." + className;
            var test = new Test {Name = testName, Filename = filename};
            Logger.DebugFormat("Found test {0} in file {1}.", test.Name, test.Filename);

            _messageBus.Publish(this, new RunTestsMessage(test));
        }
    }
}